package com.boxtelecom.services;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.jivesoftware.smack.AccountManager;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.StringUtils;

import com.boxtelecom.chatConnection.FacebookConnectSASLMechanism;
import com.boxtelecom.interfaces.IChatService;
import com.boxtelecom.interfaces.IChatServiceListener;
import com.boxtelecom.users.RosterUser;

import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

public class ChatService extends Service implements IChatService {
	
	private ConnectionConfiguration _connConfig = null;
	private XMPPConnection _xmppConnection = null;
	private ChatManager _chatManager = null;
	private Chat _chat = null;
	
	private String _contact = null;
	
	private List<IChatServiceListener> _listeners = null;
	
	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return new LocalBinder<ChatService>(this);
	//	return _myBinder;
	}

	/** 
	 * connect to the XMPP server of Facebook using X-FACEBOOK-PLATFORM
	 */
	public void serverConnection(Bundle bundle){
		
		SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM", FacebookConnectSASLMechanism.class);
		SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
		
		_connConfig = new ConnectionConfiguration("chat.facebook.com", 5222, "chat.facebook.com");
		_connConfig.setSecurityMode(SecurityMode.enabled);
		_connConfig.setSASLAuthenticationEnabled(true);
		_connConfig.setCompressionEnabled(false);
		_connConfig.setReconnectionAllowed(false);
				
		_xmppConnection = new XMPPConnection(_connConfig);
			
		new ConnectionAsyncTack().execute(bundle);
	}
	
	public String getLoggedName(){
		AccountManager myAccount = _xmppConnection.getAccountManager();
		String loginName = myAccount.getAccountAttribute("name");
		if(loginName != null){
			return loginName;
		}
		
		return null;
	}
	
	/**
	 * Create a chat dialog with the specified user
	 * @param contact the jid of the user we want to talk with
	 */
	public void createChat(String contact){
		_contact = contact;
		_chatManager = _xmppConnection.getChatManager();
		_chat = _chatManager.createChat(contact, new MessageListener() {
			@Override
			public void processMessage(Chat arg0, Message arg1) {
				// TODO Auto-generated method stub
			}
		});
	}
	
	/**
	 * Send a message to the user we are talking with
	 * @param message the message to send
	 */
	public void sendMessage(String message){
		try {
			
			Bundle data = new Bundle();
			String id = toId(_contact);
			
			data.putString("id", id);
			data.putBoolean("user", true);
			data.putBoolean("newMessage", false);
			data.putString("msg", message);
			fireIncomingMessage(data);
			
			_chat.sendMessage(message);
		} catch (XMPPException e) {
			// TODO Auto-generated catch block
			Log.d("xmpp", "xmpp error:: " + e.getMessage());
			Log.d("xmpp", "xmpp error:: " + e.getXMPPError().getCondition());
			fireError(e.getMessage());
		}
	}

	@Override
	public void addListener(IChatServiceListener listener) {
		// TODO Auto-generated method stub
		if(_listeners == null) {
			_listeners = new ArrayList<IChatServiceListener>();
		}
		if(!_listeners.contains(listener)){
			_listeners.add(listener);
		}
	}

	@Override
	public void removeListener(IChatServiceListener listener) {
		// TODO Auto-generated method stub
		if(_listeners != null) {
			_listeners.remove(listener);
		}
	}
	
	/**
	 * notify to the listeners that a message had been received from any contacts
	 * @param message the received message
	 */
	private void fireNewMessage(String message){
		if(_listeners != null){
			for(IChatServiceListener listener : _listeners){
				listener.receiveMessage(message);		
			}
		}
	}
	
	/**
	 * notify to the listeners that a the user is connected to the XMPP server
	 * @param list the roster of the connected user
	 */
	private void fireLoginFinished(ArrayList<RosterUser> list){
		if(_listeners != null){
			for(IChatServiceListener listener : _listeners){
				listener.loginFinished(list);
			}
		}
	}
	
	/**
	 * notify to the listeners that a message had been received from the user we are talking
	 * @param data id of the user of the message received, message received, a boolean specifying if the user send the message
	 * or if it received by the contact, a boolean specifying that a new message had been received
	 */
	private void fireIncomingMessage(Bundle data){
		if(_listeners != null){
			for(IChatServiceListener listener : _listeners){
				listener.incomingMessage(data);
			}
		}
	}

	/** 
	 * notify the listeners that the chat is finished
	 * @param data the id of the user + a flag specifying that the chat is finished
	 */
	private void fireChatFinished(Bundle data){
		if(_listeners != null){
			for(IChatServiceListener listener : _listeners){
				listener.chatFinished(data);
			}
		}
	}
	
	/**
	 * notify the listeners that an error occurred
	 * @param error the error
	 */
	private void fireError(String error){
		if(_listeners != null){
			for(IChatServiceListener listener : _listeners){
				listener.chatError(error);
			}
		}
	}
	
	/**
	 * notify the listeners that the presence of one contacts changed
	 * @param user the contact whom presence changed
	 */
	private void firePresenceChange(RosterUser user){
		if(_listeners != null){
			for(IChatServiceListener listener : _listeners){
				listener.presenceChange(user);
			}
		}
	}
	
	/**
	 * disconnect from the XMPP server
	 */
	public void serverDisconnection() {
		// TODO Auto-generated method stub
		try {
			if(_xmppConnection != null){
				_xmppConnection.disconnect();
			}
			Log.d("xmpp", "logged out from server");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			Log.d("error", "(xmpp) " + e.getMessage());
		}
	}
	
	/**
	 * Class that connects to the XMPP server
	 * @author Boxadmin
	 *
	 */
	private class ConnectionAsyncTack extends AsyncTask<Bundle, Void, Void> {

		@Override
		protected Void doInBackground(Bundle... params) {
			// TODO Auto-generated method stub
			final String apiKey = params[0].getString("apiKey");
			final String sessionKey = params[0].getString("sessionKey");
			final String secretKey = params[0].getString("secretKey");
			
			try {
				Log.d("xmpp", "start connection to server");
				_xmppConnection.connect();
				Log.d("xmpp", "connected to server");			
				
				Log.d("xmpp", "start authentication to server");
				_xmppConnection.login(apiKey + "|" + sessionKey, secretKey);
				Log.d("xmpp", "logged to server");
				
				final Roster roster = _xmppConnection.getRoster();
				Collection<RosterEntry> users = roster.getEntries();
				ArrayList<RosterUser> userRoster = new ArrayList<RosterUser>();
				
				Log.d("xmpp", "start getting roster");
				
				Thread.sleep(5000);
								
				for(RosterEntry user: users){
					RosterUser tmp = new RosterUser();
					tmp.setUser(user.getUser());
					tmp.setName(user.getName());
					tmp.setIdFromUser(user.getUser());
					
					if(roster.getPresence(user.getUser()).isAvailable()){
						tmp.setPresence(roster.getPresence(user.getUser()).getType().name());//, roster.getPresence(user.getUser()).getMode().name());
					}
					userRoster.add(tmp);
				}
				
				Log.d("xmpp", "roster got");
				
				fireLoginFinished(userRoster);
				
				PacketFilter chatFilter = new MessageTypeFilter(Message.Type.chat);
				
				PacketTypeFilter presenceFilter = new PacketTypeFilter(Presence.class);
//				PacketFilter userFilter = new FromContainsFilter(login)
				
				PacketListener myChatPacketListener = new PacketListener() {
					
					@Override
					public void processPacket(Packet arg0) {
						// TODO Auto-generated method stub
						Message msg = (Message) arg0;
						String user = StringUtils.parseBareAddress(msg.getFrom());
						// received a message concerning the chatting user
						if(user.equals(_contact)){
							if(msg.getBody().length()>0) {
								Log.d("chat", user + ": " + msg.getBody());
								fireNewMessage(msg.getBody());
								Bundle data = new Bundle();
								data.putString("id", toId(user));
								data.putBoolean("user", false);
								data.putBoolean("newMessage", true);
								data.putString("msg", msg.getBody());
								fireIncomingMessage(data);
							}
						} 
						// received a message concerning another user
						else {
							if(msg.getBody().length()>0) {
								Bundle data = new Bundle();
								Log.d("chat", user + ": " + msg.getBody());
								data.putString("id", toId(user));
								data.putBoolean("newMessage", true);
								data.putBoolean("user", false);
								data.putString("msg", msg.getBody());
								fireIncomingMessage(data);
							}
						}
				}
			};
			
			PacketListener myPresencePacketListener = new PacketListener() {
				
				@Override
				public void processPacket(Packet arg0) {
					// TODO Auto-generated method stub
					Presence presence = (Presence) arg0;
					Log.d("Presence", presence.getFrom() + " is now " + presence.getType());
					
					RosterUser tmp = new RosterUser();
					tmp.setUser(presence.getFrom());
					tmp.setIdFromUser(presence.getFrom());
					
					tmp.setPresence(presence.getType().name());
										
					firePresenceChange(tmp);
				}
			}; 
				
			_xmppConnection.addPacketListener(myChatPacketListener, chatFilter);
			_xmppConnection.addPacketListener(myPresencePacketListener, presenceFilter);
			
			} catch (XMPPException e) {
				// TODO Auto-generated catch block
				Log.d("error", "(xmpp) xmpp exception: " + e.getMessage());
				//Log.d("error", "(xmpp) xmpp exception: " + e.getXMPPError().getMessage());
				fireError(e.getMessage());
			} catch (Exception e) {
				Log.d("error", "(xmpp) exception:" + e.getMessage());
				fireError(e.getMessage());
			}
			
			
		
			return null;
		}		
	}

	/**
	 * method to notify that the current chat has stopped
	 */
	public void stopChat() {
		// TODO Auto-generated method stub
		Bundle data = new Bundle();
		data.putString("user", toId(_contact));
		data.putBoolean("flag", true);
		fireChatFinished(data);
		_chatManager = null;
		_chat = null;
		_contact = null;
	}
	
	/**
	 * convert a jid into a facebook id
	 * @param user the user jid to convert
	 * @return the facebook id obtained
	 */
	public String toId(String user){
		int deb_id = user.indexOf("-");
		int fin_id = user.lastIndexOf("@");
		return user.substring(deb_id + 1, fin_id);
	}
}
